<?php

// 为方便系统核心升级，二次开发中需要用到的公共函数请写在这个文件，不要去修改common.php文件

use think\facade\Cache;
use think\facade\Log;
use think\Db;

/**
 * 获取支付配置信息
 * @param  string $name  配置名
 * @param  string $group 配置分组
 * @return mixed
 */
function pay_config($name = '', $group = '')
{
    return \app\payment\model\PaymentConfig::config($name, $group);
}

/**
 * 节点解析
 * @param  string $type  类型
 * @param  string $link  链接
 * @return mixed
 */
function node_analyze($type = '', $link = '')
{

    switch ($type) {
        case 'ssr':

            $ssr_link = str_replace("ssr://", "", $link);
            $ssr_param = base64_decode(str_replace(['-', '_'], ['+', '/'], $ssr_link));

            $param_array = explode(":", $ssr_param);

            $server = $param_array[0];
            $port = $param_array[1];
            $protocol = $param_array[2];
            $method = $param_array[3];
            $obfs = $param_array[4];

            $password_and_param = explode("/?", $param_array[5]);
            $password = base64_decode(str_replace(['-', '_'], ['+', '/'], $password_and_param[0]));

            $obfsparam = "";
            $protocolparam = "";
            $remarks = "";
            $group = "";

            if (isset($password_and_param[1])) {
                $query = $password_and_param[1];
                parse_str($query, $params);

                if (isset($params['obfsparam'])) {
                    $obfsparam = base64_decode(str_replace(['-', '_'], ['+', '/'], $params['obfsparam']));
                }

                if (isset($params['protoparam'])) {
                    $protocolparam = base64_decode(str_replace(['-', '_'], ['+', '/'], $params['protoparam']));
                }

                if (isset($params['remarks'])) {
                    $remarks = base64_decode(str_replace(['-', '_'], ['+', '/'], $params['remarks']));
                }

                if (isset($params['group'])) {
                    $group = base64_decode(str_replace(['-', '_'], ['+', '/'], $params['group']));
                }
            }

            $res['assort'] = 'ShadowsocksR';
            $res['address'] = $server;
            $res['port'] = $port;
            $res['password'] = $password;
            $res['method'] = $method;
            $res['protocol'] = $protocol;
            $res['protoparam'] = $protocolparam;
            $res['obfs'] = $obfs;
            $res['obfsparam'] = $obfsparam;
            $res['remarks'] = $remarks;
            $res['group'] = $group;

            return $res;
            break;
        case 'vmess':

            // 将 Vmess 链接进行 Base64 解码
            $v2ray_base64 = substr($link, 8);
            $v2ray_decode = base64_decode(str_replace(['-', '_', '='], ['+', '/', ''], $v2ray_base64));

            // 获取 V2Ray/Vmess 节点参数
            $v2ray_info = json_decode($v2ray_decode, true);

            $server = isset($v2ray_info['address']) ? $v2ray_info['address'] : '';
            $server = isset($v2ray_info['add']) ? $v2ray_info['add'] : $server;

            $port = isset($v2ray_info['port']) ? $v2ray_info['port'] : '';

            $uuid = isset($v2ray_info['uuid']) ? $v2ray_info['uuid'] : '';
            $uuid = isset($v2ray_info['id']) ? $v2ray_info['id'] : $uuid;

            $aid = isset($v2ray_info['alter_id']) ? $v2ray_info['alter_id'] : '';
            $aid = isset($v2ray_info['aid']) ? $v2ray_info['aid'] : $aid;

            $security = isset($v2ray_info['security']) ? $v2ray_info['security'] : '';
            $security = isset($v2ray_info['method']) ? $v2ray_info['method'] : $security;
            $security = isset($v2ray_info['scy']) ? $v2ray_info['scy'] : $security;

            $network = isset($v2ray_info['network']) ? $v2ray_info['network'] : '';
            $network = isset($v2ray_info['net']) ? $v2ray_info['net'] : $network;

            $type = isset($v2ray_info['type']) ? $v2ray_info['type'] : '';

            $host = isset($v2ray_info['host']) ? $v2ray_info['host'] : '';

            $path = isset($v2ray_info['path']) ? $v2ray_info['path'] : '';

            $tls = isset($v2ray_info['tls']) ? $v2ray_info['tls'] : '';

            $sni = isset($v2ray_info['sni']) ? $v2ray_info['sni'] : '';

            $alpn = isset($v2ray_info['alpn']) ? $v2ray_info['alpn'] : '';

            $fingerprint = isset($v2ray_info['fingerprint']) ? $v2ray_info['fingerprint'] : '';
            $fingerprint = isset($v2ray_info['fp']) ? $v2ray_info['fp'] : $fingerprint;

            $remarks = isset($v2ray_info['ps']) ? $v2ray_info['ps'] : '';

            $res['assort'] = 'VMess';
            $res['remarks'] = $remarks;
            $res['address'] = $server;
            $res['port'] = $port;
            $res['uuid'] = $uuid;
            $res['aid'] = $aid;
            $res['security'] = $security;
            $res['network'] = $network;
            $res['type'] = $type;
            $res['host'] = $host;
            $res['path'] = $path;
            $res['tls'] = $tls;
            $res['sni'] = $sni;
            $res['alpn'] = $alpn;
            $res['fingerprint'] = $fingerprint;

            return $res;
            break;
        case 'vless':

            $vless_link = $link;

            // 解析UUID
            $vless_link = strtok($vless_link, "@");
            $uuid = strtok(":");

            // 解析服务器地址和端口
            $vless_link = strtok(":");
            $server = strtok("?");

            // 解析服务器端口
            $vless_link = strtok("?");
            $port = strtok("&");

            // 解析路径
            $vless_link = strtok("=");
            $path = strtok("&");

            // 解析安全性方式
            $vless_link = strtok("=");
            $security = strtok("&");

            // 解析加密方式
            $vless_link = strtok("=");
            $encryption = strtok("&");

            // 解析ALPN
            $vless_link = strtok("=");
            $alpn = strtok("&");

            // 解析Host
            $vless_link = strtok("=");
            $host = strtok("&");

            // 解析指纹
            $vless_link = strtok("=");
            $fp = strtok("&");

            // 解析连接类型
            $vless_link = strtok("=");
            $type = strtok("&");

            // 解析流量设置
            $vless_link = strtok("=");
            $flow = strtok("&");

            // 解析SNI
            $vless_link = strtok("=");
            $sni = strtok("#");

            // 解析备注
            $remark = strtok("#");

            $res['assort'] = 'VLESS';
            $res['remarks'] = $remark;
            $res['address'] = $server;
            $res['port'] = $port;
            $res['uuid'] = $uuid;
            $res['flow'] = $flow;
            $res['encryption'] = $encryption;
            $res['network'] = $type;
            $res['type'] = $type;
            $res['host'] = $host;
            $res['path'] = $path;
            $res['tls'] = '';
            $res['sni'] = $sni;
            $res['alpn'] = $alpn;
            $res['fingerprint'] = $fp;

            return $res;
            break;
        case 'ss':

            $ssLink = $link;

            // 解码SS链接
            $decodedLink = base64_decode(substr($ssLink, 5));
            // 解析参数
            $parts = explode('@', $decodedLink);
            $serverPart = $parts[1];
            $serverParts = explode(':', $serverPart);
            $serverAddress = $serverParts[0];
            $serverPort = $serverParts[1];
            $methodAndPassword = explode(':', $parts[0]);
            $encryptionMethod = $methodAndPassword[0];
            $password = $methodAndPassword[1];

            $res['assort'] = 'Shadowsocks';
            $res['remarks'] = 'Shadowsocks';
            $res['address'] = $serverAddress;
            $res['port'] = $serverPort;
            $res['encryption'] = $encryptionMethod;
            $res['password'] = $password;

            return $res;
            break;
        case 'socks':

            $res = '';
            return $res;
            break;
        case 'trojan':

            $link = str_replace('trojan://', '', $link);

            $temp = strpos($link, '@');
            $password = substr($link, 0, $temp);

            $temp = strpos($link, '#') + 1;
            $remarks = substr($link, $temp);
            $remarks = urldecode($remarks);

            $pattern = sprintf('/%s(.*?)%s/', preg_quote('@', '/'), preg_quote(':', '/'));
            preg_match($pattern, $link, $matches);
            $address = isset($matches[1]) ? $matches[1] : '';

            $pattern = sprintf('/%s(.*?)%s/', preg_quote(':', '/'), preg_quote('?', '/'));
            preg_match($pattern, $link, $matches);
            $port = isset($matches[1]) ? $matches[1] : '';

            $temp = strpos($link, '#');
            $link = substr($link, 0, $temp);

            // 解析查询参数
            $queryPos = strpos($link, '?');
            $queryInfo = substr($link, $queryPos + 1);
            parse_str($queryInfo, $queryParams);

            $security = isset($queryParams['security']) ? $queryParams['security'] : '';
            $alpn = isset($queryParams['alpn']) ? $queryParams['alpn'] : '';
            $headerType = isset($queryParams['encryption']) ? $queryParams['encryption'] : '';
            $headerType = isset($queryParams['headerType']) ? $queryParams['headerType'] : $headerType;
            $fp = isset($queryParams['fp']) ? $queryParams['fp'] : '';
            $type = isset($queryParams['type']) ? $queryParams['type'] : '';
            $sni = isset($queryParams['sni']) ? $queryParams['sni'] : '';

            $res['assort'] = 'Trojan';
            $res['remarks'] = $remarks;
            $res['address'] = $address;
            $res['port'] = $port;
            $res['password'] = $password;
            $res['flow'] = '';
            $res['network'] = $type;
            $res['type'] =  $headerType;
            $res['host'] = '';
            $res['path'] = '';
            $res['tls'] = $security;
            $res['sni'] = $sni;
            $res['alpn'] = $alpn;
            $res['fingerprint'] = $fp;

            return $res;
            break;
        default:
            return;
    }
}

/**
 * 节点生成
 * @param  string $type  类型
 * @param  string $data  配置
 * @return mixed
 */
function node_generate($type = '', $data = '')
{

    switch ($type) {
        case 'ShadowsocksR':

            $server = $data['address'];
            $server_port = $data['port'];
            $protocol = $data['protocol'];
            $method = $data['method'];
            $obfs = $data['obfs'];
            $password = $data['password'];
            $obfsparam = $data['obfsparam'];
            $protoparam = $data['protoparam'];
            $remarks = $data['remarks'];

            $ssrLink = "{$server}:{$server_port}:{$protocol}:{$method}:{$obfs}:" . base64_encode($password) . "/?" . http_build_query([
                'obfsparam' => base64_encode(str_replace(['+', '/'], ['-', '_'], $obfsparam)),
                'protoparam' => base64_encode(str_replace(['+', '/'], ['-', '_'], $protoparam)),
                'remarks' => base64_encode(str_replace(['+', '/'], ['-', '_'], $remarks))
            ]);

            $ssrLink = 'ssr://' . base64_encode($ssrLink);
            return $ssrLink;
        case 'VMess':

            $address = $data['address']; // 修改成您的服务器地址
            $port = $data['port']; // 修改成您的服务器端口号
            $id = $data['uuid']; // 修改成您的用户ID
            $aid = $data['aid']; // 额外的 ID，它是一个数字，可以更改
            $security = $data['security']; // 数据传输时的加密方式，默认为'auto'，也可以设置为'aes-128-gcm'、'chacha20-poly1305'、'none'等。
            $network = $data['network']; // 传输方式，tcp 或 kcp 传输，默认为'tcp'，也可以设置为'kcp'等。
            $path = $data['path']; // WebSocket 的路径，可以是任何字符串。默认为空字符串。

            $tcp = array(
                "header" => array(
                    "type" => "http",
                    "request" => array(
                        "version" => "1.1",
                        "method" => "GET",
                        "path" => array("/", "abc", 123),
                        "headers" => array(
                            "Host" => "www.google.com",
                            "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0",
                            "Accept-Encoding" => "gzip, deflate",
                            "Accept-Language" => "en-US,en;q=0.5",
                            "Connection" => "keep-alive"
                        )
                    ),
                    "response" => array(
                        "version" => "1.1",
                        "status" => "200",
                        "reason" => "OK",
                        "headers" => array(
                            "Content-Type" => "text/html; charset=UTF-8",
                            "Content-Encoding" => "gzip",
                            "Server" => "Apache",
                            "Content-Length" => "183"
                        )
                    )
                ),
                "noDelay" => true,
                "keepAlive" => true,
                "reusePort" => false
            );

            $kcp = array(
                "mtu" => 1400,
                "tti" => 50,
                "uplinkCapacity" => 12,
                "downlinkCapacity" => 12,
                "congestion" => false,
                "readBufferSize" => 2,
                "writeBufferSize" => 2
            );

            $json = array(
                "v" => "2",
                "ps" => $data['remarks'],
                "add" => $address,
                "port" => $port,
                "id" => $id,
                "aid" => $aid,
                "security" => $security,
                "net" => $network,
                "type" => $data['type'],
                "host" => $data['host'],
                "path" => $path,
                "tls" => $data['tls'],
                "sni" => $data['sni'],
                // "wsHeaders" => array(
                //     "Host" => "www.google.com",
                //     "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0",
                //     "Accept-Encoding" => "",
                //     "Accept-Language" => "en-US,en;q=0.5",
                //     "Connection" => ""
                // ),
                // "tcp" => $tcp,
                // "kcp" => $kcp,
                // "http" => array(
                //     "path" => "",
                //     "host" => array()
                // ),
                // "mux" => array(
                //     "enabled" => false,
                //     "concurrency" => 8
                // )
            );

            $url = 'vmess://' . base64_encode(json_encode($json));

            return $url;
            break;
        case 'Trojan':

            $string = '';
            if ($data['tls']) {
                $string .= "security={$data['tls']}";
            }
            if ($data['alpn']) {
                $string .= "&alpn={$data['alpn']}";
            }
            if ($data['type']) {
                $string .= "&encryption={$data['type']}";
            }
            if ($data['network']) {
                $string .= "&type={$data['network']}";
            }
            if ($data['sni']) {
                $string .= "&sni={$data['sni']}";
            }
            if ($data['remarks']) {
                $remarks = '#' . urlencode($data['remarks']);
                $string .= "$remarks";
            }

            $trojanLink = "trojan://{$data['password']}@{$data['address']}:{$data['port']}?$string";
            return $trojanLink;
        default:
            return;
    }
}

/**
 * 获取支付配置信息
 * @param  string $name  配置名
 * @param  string $group 配置分组
 * @return mixed
 */
function vpn_config($name = '', $group = '')
{
    return \app\vpn\model\VpnConfig::config($name, $group);
}

/**
 * 获取用户信息
 * @param  string $id      用户id
 * @param  string $update  更新
 * @return mixed
 */
function get_user($id = '', $update = false)
{
    $set_time = 86400;
    $redis = Cache::store('redis');
    $vpn_uid = 'vpn_uid' . $id;
    if ($id) {
        if ($update) {
            $user = Db::name('admin_user')->where(['id' => $id])->find();
            $redis->set($vpn_uid, $user, $set_time);
        } else {

            $user = $redis->get($vpn_uid);
            if ($user) {
                return $user;
            } else {
                $user = Db::name('admin_user')->where(['id' => $id])->find();
                $redis->set($vpn_uid, $user, $set_time);
            }
        }
        return $user;
    }
    return false;
}

/**
 * 订单成功
 * @param  string $order_id  订单号
 * @return mixed
 */
function order_success($order_id = '')
{
    try {
        if ($order_id) {
            $data = Db::name('payment_order')->where(['order_id' => $order_id])->find();

            $shop = Db::name('vpn_shop')->where(['cid' => $data['shop_id']])->find();
            $user = Db::name('admin_user')->where(['id' => $data['user_id']])->find();

            $user_update = [];
            $day = $shop['day'] * 86400;
            if ($shop['mode'] == 0) {
                $user['expire_time'] = $user['expire_time'] ? $user['expire_time'] : 0;
                if ($user['expire_time'] < time()) {
                    $update_time = time() + $day;
                } else {
                    $update_time = $user['expire_time'] + $day;
                }
                $user_update['expire_time'] = $update_time;
            }

            if ($shop['mode'] == 1) {
                $user['high_expire_time'] = $user['high_expire_time'] ? $user['high_expire_time'] : 0;
                if ($user['high_expire_time'] < time()) {
                    $update_time = time() + $day;
                } else {
                    $update_time = $user['high_expire_time'] + $day;
                }
                $user_update['high_expire_time'] = $update_time;
            }
            $user_update['update_time'] = time();
            $user_update_status = Db::name('admin_user')->where(['id' => $data['user_id']])->update($user_update);
            if ($user_update_status) {
                return true;
            }

            return false;
        }

        return false;
    } catch (\Exception $e) {
        vpn_error_log(110020, input(), $e->getMessage(), false);
        return false;
    }
}

/**
 * 获取配置信息
 * @param  string $name  配置名
 * @param  string $group 配置分组
 * @return mixed
 */
function virtual_config($name = '', $group = '')
{
    return \app\virtual\model\VirtualConfig::config($name, $group);
}

/**
 * 获取配置信息
 * @param  string $name  配置名
 * @param  string $group 配置分组
 * @return mixed
 */
function bot_config($name = '', $group = '')
{
    return \app\bot\model\BotConfig::config($name, $group);
}

/**
 * 获取下级商户信息
 * @param  string $id    商户号
 * @return mixed
 */
function get_sub_merchant($id = 0)
{
    $data = Db::name('payment_merchant_config')->where([])->field('id,sup_merchant')->select();
    $arr = array();
    $mids = array($id);
    do {
        $othermids = array();
        $state = false;
        foreach ($mids as $valueone) {
            foreach ($data as $key => $valuetwo) {
                if ($valuetwo['sup_merchant'] == $valueone) {
                    $arr[] = $valuetwo['id'];          // 找到我的下级立即添加到最终结果中
                    $othermids[] = $valuetwo['id'];    // 将我的下级id保存起来用来下轮循环他的下级
                    array_splice($data, $key, 1);      // 从所有会员中删除他
                    $state = true;
                }
            }
        }
        $mids = $othermids;                            // foreach中找到的我的下级集合,用来下次循环
    } while ($state == true);

    return $arr;
}

/**
 * 获取指定下级商户层级信息
 * @param  string $data         数据配置名
 * @param  string $sup_merchant 上级代理号
 * @param  string $level        层级
 * @return mixed
 */
function get_sub_merchant_tree($data = [], $sup_merchant = 0, $level = 0)
{
    $tree = [];
    foreach ($data as $row) {
        if ($row['sup_merchant'] == $sup_merchant) {
            $row['level'] = $level;
            $row['pid'] = get_sub_merchant_tree($data, $row['id'], $level + 1);
            $tree[] = $row;
        }
    }
    return $tree;
}

/**
 * 获取所有已经支付配置完成的商户号和名称
 * @param  string $merchant_id 商户号
 * @return array
 */
function all_merchant($merchant_id = '')
{
    return \app\payment\model\PaymentConfig::allMerchantConfig($merchant_id);
}

/**
 * 获取二维数组中存在的一维数组
 * @param  array $one_array 一维数组
 * @param  array $two_array 二维数组
 * @return array      
 */
function getOneTwoArray($one_array = [], $two_array = [])
{
    if (!is_array($one_array) || !is_array($two_array)) {
        return [];
    }
    $arr = [];
    foreach ($one_array as $value) {
        if (isset($two_array[$value])) {
            $arr[$value] = $two_array[$value];
        }
    }
    return $arr;
}

/**
 * 根据商户号获取商户号下的所有应用id
 * @param  string $merchant_id  商户号
 * @param  string $status       应用状态
 * @return array      
 */
function merchant_appid($merchant_id = '', $status = '1')
{
    $merchant_ids = $merchant_id ? ['merchant_id' => $merchant_id] : [];
    $statuss = $status ? ['status' => $status] : [];
    $map = array_merge($merchant_ids, $statuss);
    return Db::name('payment_app')->where($map)->column('id');
}

/**
 * 根据商户号获取昵称（商户号名称）
 * @param  string $merchant_id 商户号
 * @return string      
 */
function get_user_nickname($merchant_id = '')
{
    return Db::name('admin_user')->where(['merchant_id' => $merchant_id])->value('nickname');
}

/**
 * 根据商户号获取密钥
 * @param  string $merchant_id 商户号
 * @return string      
 */
function get_merchant_key($merchant_id = '')
{
    return Db::name('payment_merchant_config')->where(['id' => $merchant_id])->value('key');
}

/**
 * 格式美化
 * @param  string $json 内容
 * @param  string $html 格式
 * @return string
 */
function format_nice($json = '', $html = false)
{

    $array = json_decode($json, true);
    if (is_array($array)) {
        $temp = to_urlencode($array);
        $str_json = json_encode($temp, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $json = urldecode($str_json);
    } else {
        $json = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($matches) {
            return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
        }, $json);
    }

    $tabcount = 0;
    $result = '';
    $inquote = true;
    $ignorenext = false;
    if ($html) {
        $tab = "   ";
        $newline = "<br/>";
    } else {
        $tab = "\t";
        $newline = "\n";
    }
    for ($i = 0; $i < strlen($json); $i++) {
        $char = $json[$i];
        if ($ignorenext) {
            $result .= $char;
            $ignorenext = true;
        } else {
            switch ($char) {
                case '{':
                    $tabcount++;
                    $result .= $char . $newline . str_repeat($tab, $tabcount);
                    break;
                case '}':
                    $tabcount--;
                    $tabcount = $tabcount > 0 ? $tabcount : 0;
                    $result = trim($result) . $newline . str_repeat($tab, $tabcount) . $char;
                    break;
                case '[':
                    $tabcount++;
                    $result .= $char . $newline . str_repeat($tab, $tabcount);
                    break;
                case ']':
                    $tabcount--;
                    $tabcount = $tabcount > 0 ? $tabcount : 0;
                    $result = trim($result) . $newline . str_repeat($tab, $tabcount) . $char;
                    break;
                case ',':
                    $result .= $char . $newline . str_repeat($tab, $tabcount);
                    break;
                case '"':
                    $inquote = !$inquote;
                    $result .= $char;
                    break;
                case '\\':
                    if ($inquote) {
                        $ignorenext = true;
                    }
                    $result .= $char;
                    break;
                default:
                    $result .= $char;
            }
        }
    }
    return stripslashes($result);
}

/**
 * 将数组里面包含有中文的字串,用urlencode转换格式返回 
 * @param array $array   数组
 */
function to_urlencode($array = [])
{
    $temp = array();
    if (is_array($array)) {
        foreach ($array as $k => $v) {
            $temp[$k] = $v;
            if (is_array($temp[$k])) {
                $temp[$k] = to_urlencode($temp[$k]);
            } else {
                $temp[$k] = urlencode($v);
            }
        }
    } else {
        $temp = $array;
    }
    return $temp;
}

/**
 * curl访问
 * @param  string   $url       地址
 * @param  array    $data      数据
 * @param  string   $type      请求类型
 * @param  int      $timeout   超时
 * @param  array    $header    设置 HTTP 头字段的数组
 * @param  array    $useragent 在HTTP请求中包含一个"User-Agent: "头的字符串
 * @param  string   $err_msg   错误提示
 * @param  array    $cert_info 证书
 * @return string
 */
function go_curl($url, $data = '', $type = 'get', $timeout = 3, $header = '', $useragent = '', $err_msg = null, $cert_info = array())
{
    $type = strtolower($type);
    if ($type == 'get' && is_array($data)) {
        $data = http_build_query($data);
    }
    $option = array();
    if ($type == 'post') {
        $option[CURLOPT_POST] = 1;
    }
    if ($data) {
        if ($type == 'post') {
            $option[CURLOPT_POSTFIELDS] = $data;
        } elseif ($type == 'get') {
            $url = strpos($url, '?') !== false ? $url . '&' . $data : $url . '?' . $data;
        }
    }
    $option[CURLOPT_URL] = $url;
    $option[CURLOPT_FOLLOWLOCATION] = true;
    $option[CURLOPT_MAXREDIRS] = 4;
    $option[CURLOPT_RETURNTRANSFER] = true;
    $option[CURLOPT_TIMEOUT] = $timeout;
    // 设置 HTTP 头字段的数组
    if ($header) {
        $option[CURLOPT_HTTPHEADER] = $header;
    }
    // 在HTTP请求中包含一个"User-Agent: "头的字符串
    if ($useragent) {
        $option[CURLOPT_USERAGENT] = $useragent;
    }
    // 设置证书信息
    if (!empty($cert_info) && !empty($cert_info['cert_file'])) {
        $option[CURLOPT_SSLCERT] = $cert_info['cert_file'];
        $option[CURLOPT_SSLCERTPASSWD] = $cert_info['cert_pass'];
        $option[CURLOPT_SSLCERTTYPE] = $cert_info['cert_type'];
    }
    // 设置CA
    if (!empty($cert_info['ca_file'])) {
        $option[CURLOPT_SSL_VERIFYPEER] = 1;  // 要验证的交换证书可以在 CURLOPT_CAINFO 选项中设置
        $option[CURLOPT_CAINFO] = $cert_info['ca_file'];
    } else {
        $option[CURLOPT_SSL_VERIFYHOST] = 0;  // 检查服务器SSL证书
        $option[CURLOPT_SSL_VERIFYPEER] = 0;  // FALSE 禁止 cURL 验证对等证书
    }
    $ch = curl_init();
    curl_setopt_array($ch, $option);
    $response = curl_exec($ch);
    $curl_no = curl_errno($ch);
    $curl_err = curl_error($ch);
    curl_close($ch);
    // error_log
    if ($curl_no > 0) {
        if ($err_msg == null) {
            $err_msg = '请求地址：' . $url . '  错误编号：' . '(' . $curl_no . ')' . '  错误字符串：' . $curl_err;
        }
        Log::record("curl错误：{$err_msg}", 'log');
        // payment_error_log(500000, $err_msg, $data, false);
    }
    return $response;
}

/**
 * 生成随机字符串
 * @param  int     $length  生成长度
 * @param  int     $type    生成类型：0-小写字母+数字，1-数字，2-大写字母，3-小写字母，4-小写+大写字母，5-小写+大写+数字
 * @return string
 */
function random($length = 10, $type = 1)
{

    $a = 'abcdefghijklmnopqrstuvwxyz';
    $A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $n = '0123456789';

    switch ($type) {
        case 1:
            $chars = $n;
            break;
        case 2:
            $chars = $A;
            break;
        case 3:
            $chars = $a;
            break;
        case 4:
            $chars = $a . $A;
            break;
        case 5:
            $chars = $a . $A . $n;
            break;
        default:
            $chars = $a . $n;
    }

    $str = '';
    $strlen = strlen($chars) - 1;
    for ($i = 0; $i < $length; $i++) {
        $str .= $chars[mt_rand(0, $strlen)];
    }
    return $str;
}

/**
 * 系统错误邮箱发送
 * @param  string  $subject 邮件主题标题
 * @param  string  $body    邮件内容
 * @param  string  $tomail  是否发送邮件
 * @return boolean
 */
function send_mail_error($subject = '', $body = '', $tomail = true)
{

    if ($tomail) {
        $redis = Cache::store('redis');
        // 邮箱发送错误开关
        $error_email_open = $redis->get('error_email_open');
        if ($error_email_open && $error_email_open['status']) {
            // 错误邮箱列表
            $error_email_list = $redis->get('error_email_list');
            $value = $error_email_list['value'];
            if (is_array($value) && !empty($value)) {
                foreach ($value as $email) {
                    $resdata = send_mail($email, $email, $subject, $body);
                    if ($resdata !== true) {
                        Db::name('payment_log')->insert(['code' => 15999, 'title' => "邮箱发送失败{$email}", 'remark' => $resdata, 'create_time' => time()]);
                        break;
                    }
                }
            }
        }
    }
    return true;
}

/**
 * 系统邮件发送函数
 * @param  string  $tomail     接收邮件者邮箱
 * @param  string  $name       接收邮件者名称
 * @param  string  $subject    邮件主题标题
 * @param  string  $body       邮件内容
 * @param  string  $attachment 附件列表
 * @return boolean
 */
function send_mail($tomail, $name = 'abc', $subject = '', $body = '', $attachment = null)
{

    $mail = new \PHPMailer\PHPMailer\PHPMailer();         // 实例化PHPMailer对象
    //Server settings
    $mail->CharSet = 'UTF-8';                             // 设定邮件编码，默认ISO-8859-1，如果发中文此项必须设置，否则乱码
    $mail->Timeout = 1;                                   // 服务器在几秒钟内超时
    $mail->SMTPDebug = 0;                                 // 启用调试输出
    $mail->isSMTP();                                      // 设置邮件收件人使用SMTP
    $mail->Host = 'smtp.163.com';                         // 指定主和备份SMTP服务器
    $mail->SMTPAuth = true;                               // 启用SMTP认证
    $mail->Username = '13797031475@163.com';              // SMTP username
    $mail->Password = 'h147258';                          // SMTP password
    $mail->SMTPSecure = 'ssl';                            // 启用TLS加密，“SSL”也被接受
    $mail->Port = 465;                                    // 连接到TCP端口
    //Recipients
    $mail->setFrom('13797031475@163.com', 'abc');
    $mail->addAddress($tomail, $name);                    // 添加收件人
    $mail->addAddress($tomail);                           // 名称是可选的
    $mail->addReplyTo($tomail, $name);
    $mail->addCC($tomail);
    $mail->addBCC($tomail);
    //Attachments   
    if (is_array($attachment) && !empty($attachment)) {   // 添加附件
        foreach ($attachment as $file) {
            is_file($file) && $mail->AddAttachment($file);
        }
    }
    //Content
    $mail->isHTML(true);                                  // 将电子邮件格式设置为HTML
    $mail->Subject = $subject;
    $mail->Body = $body;
    $mail->AltBody = $body;

    return $mail->send() ? true : $mail->ErrorInfo;
}

/**
 * 系统手机短信发送函数
 * @param  string  $mobile     手机号
 * @param  string  $code       验证码
 * @return boolean
 */
function send_mobile($mobile, $code = '')
{
    // 阿里短信服务
    $Duanxin = \Aliyun\Duanxin\SendSms::sendSms($mobile, $code);
    if ($Duanxin->Code != 'OK') {
        payment_error_log(200000, '阿里短信服务错误', json_encode($Duanxin), false);
    }
    return $Duanxin->Code == 'OK' ? true : $Duanxin->Message;
}

/**
 * 支付错误日志记录
 * @param  string   $code        错误编码
 * @param  string   $temp        自定义数据
 * @param  string   $msg         异常信息
 * @param  boolean  $tomail      是否发送邮件
 * @param  boolean  $write       是否写入错误日志
 * @param  string   $title       标题
 * @return boolean
 */
function payment_log($code = '', $temp = '', $msg = '', $tomail = true, $write = true, $title = '')
{
    switch ($code) {

        case '100000':
            $title = $title ?: "支付{$code}，商户号今天充值的支付类型金额配置总数超过了商户号今天最多充值支付类型金额总数";
            break;
        case '100002':
            $title = $title ?: "支付{$code}，创建平台订单表失败，可能是商户订单号重复";
            break;
        case '100004':
            $title = $title ?: "支付{$code}，支付公司子商户号今天充值的支付类型金额总数超过了支付子公司配置今天最多充值支付类型金额总数";
            break;
        case '100006':
            $title = $title ?: "支付{$code}，商户号连续未支付订单报警";
            break;
        case '100008':
            $title = $title ?: "支付{$code}，商户号的支付类型对应的所有支付公司，全部关闭";
            break;

        case '100010':
            $title = $title ?: "支付{$code}，商户号今天代付的金额配置总数超过了商户号今天最多代付金额总数";
            break;
        case '100012':
            $title = $title ?: "支付{$code}，创建代付订单表失败，可能是商户订单号重复";
            break;
        case '100014':
            $title = $title ?: "支付{$code}，商户号暂无可用银行卡代付通道";
            break;

        case '100016':
            $title = $title ?: "支付{$code}，支付宝代付超过金额配置额度";
            break;
        case '100018':
            $title = $title ?: "支付{$code}，创建支付宝代付订单表失败，可能是商户订单号重复";
            break;
        case '100020':
            $title = $title ?: "支付{$code}，商户号暂无可用支付宝代付通道";
            break;
        case '100022':
            $title = $title ?: "支付{$code}，创建退款订单表失败，可能是商户订单号重复";
            break;

        case '110000':
            $title = $title ?: "支付{$code}，第三方支付公司通知失败";
            break;

        case '120000':
            $title = $title ?: "支付{$code}，第三方代付公司通知失败";
            break;

        case '130000':
            $title = $title ?: "支付{$code}，第三方提现公司通知失败";
            break;


        default:
            $title = $title ?: "错误编码没有匹配";
            break;
    }
    $temp = is_array($temp) ? json_encode($temp, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $temp;
    $msg = is_array($msg) ? json_encode($msg, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $msg;
    $one = '自定义数据：' . $temp . PHP_EOL . PHP_EOL . '异常消息：' . $msg . PHP_EOL . PHP_EOL;
    $two = 'server参数：' . json_encode(request()->server(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $remark = $one . $two;
    // 写入错误日志
    if ($write) {
        Db::name('payment_log')->insert(['code' => $code, 'title' => $title, 'remark' => $remark, 'create_time' => time()]);
    }
    // 发送邮件
    if ($tomail) {
        send_mail_error($title, $remark);
    }
    return true;
}

/**
 * 支付重要错误日志记录
 * @param  string   $code        错误编码
 * @param  string   $temp        自定义数据
 * @param  string   $msg         异常信息
 * @param  boolean  $tomail      是否发送邮件
 * @param  boolean  $write       是否写入错误日志
 * @param  string   $title       标题
 * @return boolean
 */
function payment_error_log($code = '', $temp = '', $msg = '', $tomail = true, $write = true, $title = '')
{
    switch ($code) {

        case '500000':
            $title = $title ?: "支付{$code}，curl错误";
            break;

        case '600000':
            $title = $title ?: "支付{$code}，阿里语音或短信错误";
            break;
        case '600002':
            $title = $title ?: "支付{$code}，创建支付订单更新失败";
            break;
        case '600004':
            $title = $title ?: "支付{$code}，创建支付订单异常";
            break;

        case '600006':
            $title = $title ?: "支付{$code}，查询订单异常";
            break;

        case '600008':
            $title = $title ?: "支付{$code}，代付扣除商户余额失败";
            break;

        case '600010':
            $title = $title ?: "支付{$code}，更新银行卡代付订单表失败";
            break;
        case '600012':
            $title = $title ?: "支付{$code}，扣除商户代付余额还原失败（重要）";
            break;
        case '600014':
            $title = $title ?: "支付{$code}，更新银行卡代付订单表失败";
            break;
        case '600016':
            $title = $title ?: "支付{$code}，银行卡代付扣除商户代付余额还原失败（重要）";
            break;
        case '600018':
            $title = $title ?: "支付{$code}，创建银行卡代付异常";
            break;

        case '600020':
            $title = $title ?: "支付{$code}，代付扣除商户余额失败";
            break;
        case '600022':
            $title = $title ?: "支付{$code}，更新支付宝代付订单表失败";
            break;
        case '600024':
            $title = $title ?: "支付{$code}，支付宝代付扣除商户代付余额还原失败（重要）";
            break;
        case '600026':
            $title = $title ?: "支付{$code}，更新支付宝代付订单表失败";
            break;
        case '600028':
            $title = $title ?: "支付{$code}，支付宝代付扣除商户代付余额还原失败（重要）";
            break;
        case '600030':
            $title = $title ?: "支付{$code}，创建支付宝代付异常";
            break;

        case '600032':
            $title = $title ?: "支付{$code}，代付余额查询订单异常";
            break;

        case '600034':
            $title = $title ?: "支付{$code}，调用第三方订单异常";
            break;

        case '600036':
            $title = $title ?: "支付{$code}，支付跳转地址异常";
            break;

        case '600038':
            $title = $title ?: "支付{$code}，封装跳转网址异常";
            break;


        case '610000':
            $title = $title ?: "支付{$code}，商户钱包失败（重点），请处理";
            break;
        case '610001':
            $title = $title ?: "支付{$code}，商户费率配置错误（重点），请处理";
            break;
        case '610002':
            $title = $title ?: "支付{$code}，子公司费率配置错误（重点），请处理";
            break;
        case '610003':
            $title = $title ?: "支付{$code}，更新商户钱包金额失败（重点），请手动给商户加缺失的钱和加来源次数";
            break;
        case '610004':
            $title = $title ?: "支付{$code}，添加商户钱包金额失败（重点），请处理";
            break;


        case '620000':
            $title = $title ?: "支付{$code}，涉嫌修改第三方支付公司参数，关闭";
            break;
        case '620001':
            $title = $title ?: "支付{$code}，订单通知有误1，原因可能是支付公司在一秒中多次通知相同订单";
            break;
        case '620002':
            $title = $title ?: "支付{$code}，订单通知有误2，原因可能是支付公司在一秒中多次通知相同订单";
            break;
        case '620003':
            $title = $title ?: "支付{$code}，更新平台订单表状态失败1";
            break;
        case '620004':
            $title = $title ?: "支付{$code}，更新平台订单表状态失败2，无数据变化";
            break;
        case '620005':
            $title = $title ?: "支付{$code}，可能是第三方用了(多台服务器通知)，也有可能是第一次支付公司通知出现异常，状态已经修改，但是没有返回success";
            break;
        case '620006':
            $title = $title ?: "支付{$code}，订单通知有误3，原因可能是支付公司在一秒中多次通知相同订单";
            break;
        case '620007':
            $title = $title ?: "支付{$code}，通知商户失败同时插入通知表也失败";
            break;


        case '630000':
            $title = $title ?: "支付{$code}，涉嫌修改第三方代付公司参数，关闭";
            break;
        case '630001':
            $title = $title ?: "支付{$code}，订单通知有误1，原因可能是代付公司在一秒中多次通知相同订单";
            break;
        case '630002':
            $title = $title ?: "支付{$code}，订单通知有误2，原因可能是代付公司在一秒中多次通知相同订单";
            break;
        case '630003':
            $title = $title ?: "支付{$code}，更新代付订单表状态失败1";
            break;
        case '630004':
            $title = $title ?: "支付{$code}，更新代付订单表状态失败2，无数据变化";
            break;
        case '630005':
            $title = $title ?: "支付{$code}，可能是第三方用了(多台服务器通知)，也有可能是第一次代付公司通知出现异常，状态已经修改，但是没有返回success";
            break;
        case '630006':
            $title = $title ?: "支付{$code}，订单通知有误3，原因可能是代付公司在一秒中多次通知相同订单";
            break;
        case '630007':
            $title = $title ?: "支付{$code}，代付失败返回代付余额错误";
            break;
        case '630008':
            $title = $title ?: "支付{$code}，代付插入通知数据库失败";
            break;


        case '640000':
            $title = $title ?: "支付{$code}，涉嫌修改第三方代付公司参数，关闭";
            break;
        case '640001':
            $title = $title ?: "支付{$code}，订单通知有误1，原因可能是代付公司在一秒中多次通知相同订单";
            break;
        case '640002':
            $title = $title ?: "支付{$code}，订单通知有误2，原因可能是代付公司在一秒中多次通知相同订单";
            break;
        case '640003':
            $title = $title ?: "支付{$code}，更新代付订单表状态失败1";
            break;
        case '640004':
            $title = $title ?: "支付{$code}，更新代付订单表状态失败2，无数据变化";
            break;
        case '640005':
            $title = $title ?: "支付{$code}，可能是第三方用了(多台服务器通知)，也有可能是第一次代付公司通知出现异常，状态已经修改，但是没有返回success";
            break;
        case '640006':
            $title = $title ?: "支付{$code}，订单通知有误3，原因可能是代付公司在一秒中多次通知相同订单";
            break;
        case '640007':
            $title = $title ?: "支付{$code}，代付失败返回代付余额错误";
            break;
        case '640008':
            $title = $title ?: "支付{$code}，代付插入通知数据库失败";
            break;


        case '650000':
            $title = $title ?: "支付{$code}，涉嫌修改第三方提现公司参数，关闭";
            break;
        case '650001':
            $title = $title ?: "支付{$code}，订单通知有误1，原因可能是提现公司在一秒中多次通知相同订单";
            break;
        case '650002':
            $title = $title ?: "支付{$code}，订单通知有误2，原因可能是提现公司在一秒中多次通知相同订单";
            break;
        case '650003':
            $title = $title ?: "支付{$code}，更新提现订单表状态失败1";
            break;
        case '650004':
            $title = $title ?: "支付{$code}，更新提现订单表状态失败2，无数据变化";
            break;
        case '650005':
            $title = $title ?: "支付{$code}，可能是第三方用了(多台服务器通知)，也有可能是第一次提现公司通知出现异常，状态已经修改，但是没有返回success";
            break;
        case '650006':
            $title = $title ?: "支付{$code}，订单通知有误3，原因可能是提现公司在一秒中多次通知相同订单";
            break;


        case '700000':
            $title = $title ?: "支付{$code}，每天代付统计失败";
            break;
        case '700001':
            $title = $title ?: "支付{$code}，商户号已经跑过指定代付日期数据";
            break;
        case '700002':
            $title = $title ?: "支付{$code}，商户插入每天代付统计数据失败";
            break;
        case '700003':
            $title = $title ?: "支付{$code}，每天代付统计消耗时间和内存";
            break;

        case '710000':
            $title = $title ?: "支付{$code}，每天定时跑充值订单异常";
            break;
        case '710001':
            $title = $title ?: "支付{$code}，商户号已经跑过指定日期数据";
            break;
        case '710002':
            $title = $title ?: "支付{$code}，商户号支付类型费率配置有误";
            break;
        case '710003':
            $title = $title ?: "支付{$code}，商户插入每天来源订单数据失败";
            break;
        case '710004':
            $title = $title ?: "支付{$code}，商户钱包更新数据失败";
            break;
        case '710005':
            $title = $title ?: "支付{$code}，商户钱包插入数据失败";
            break;
        case '710006':
            $title = $title ?: "支付{$code}，每天统计消耗时间和内存";
            break;

        case '720000':
            $title = $title ?: "支付{$code}，每天代理统计失败";
            break;
        case '720001':
            $title = $title ?: "支付{$code}，代理已经跑过指定日期数据";
            break;
        case '720002':
            $title = $title ?: "支付{$code}，第一层代理支付类型费率配置有误";
            break;
        case '720003':
            $title = $title ?: "支付{$code}，第一层代理插入每天来源订单数据失败";
            break;
        case '720004':
            $title = $title ?: "支付{$code}，第一层代理插入每天来源订单数据失败";
            break;
        case '720005':
            $title = $title ?: "支付{$code}，第一层代理钱包更新数据失败";
            break;
        case '720006':
            $title = $title ?: "支付{$code}，第二层代理支付类型费率配置有误";
            break;
        case '720007':
            $title = $title ?: "支付{$code}，第二层代理插入每天来源订单数据失败";
            break;
        case '720008':
            $title = $title ?: "支付{$code}，第二层代理插入每天来源订单数据失败";
            break;
        case '720009':
            $title = $title ?: "支付{$code}，第二层代理钱包更新数据失败";
            break;

        case '730000':
            $title = $title ?: "支付{$code}，代理支付通道返利异常";
            break;
        case '730001':
            $title = $title ?: "支付{$code}，代理通道返利已经跑过指定日期数据";
            break;
        case '730002':
            $title = $title ?: "支付{$code}，代理通道没有配置";
            break;
        case '730003':
            $title = $title ?: "支付{$code}，商户插入每天代理通道返利数据失败";
            break;
        case '730004':
            $title = $title ?: "支付{$code}，商户钱包更新数据失败";
            break;
        case '730005':
            $title = $title ?: "支付{$code}，商户钱包插入数据失败";
            break;

        case '740000':
            $title = $title ?: "支付{$code}，代理代付通道返利异常";
            break;
        case '740001':
            $title = $title ?: "支付{$code}，代理代付通道返利已经跑过指定日期数据";
            break;
        case '740002':
            $title = $title ?: "支付{$code}，代理代付通道没有配置";
            break;
        case '740003':
            $title = $title ?: "支付{$code}，商户插入每天代理代付通道返利数据失败";
            break;
        case '740004':
            $title = $title ?: "支付{$code}，商户钱包更新数据失败";
            break;
        case '740005':
            $title = $title ?: "支付{$code}，商户钱包插入数据失败";
            break;

        case '750000':
            $title = $title ?: "支付{$code}，每天定时跑VIP用户数据异常";
            break;
        case '750001':
            $title = $title ?: "支付{$code}，每天统计VIP用户消耗时间和内存";
            break;

        case '760000':
            $title = $title ?: "支付{$code}，多次通知错误";
            break;
        case '760001':
            $title = $title ?: "支付{$code}，多次通知:在一秒中多次通知相同订单";
            break;
        case '760002':
            $title = $title ?: "支付{$code}，多次通知成功更新状态错误";
            break;
        case '760003':
            $title = $title ?: "支付{$code}，多次通知失败更新状态错误";
            break;

        case '770000':
            $title = $title ?: "支付{$code}，代付查询通知错误";
            break;
        case '770001':
            $title = $title ?: "支付{$code}，多次通知:在一秒中多次通知相同订单";
            break;
        case '770002':
            $title = $title ?: "支付{$code}，代付子公司配置不存在";
            break;
        case '770003':
            $title = $title ?: "支付{$code}，代付订单更新状态错误";
            break;
        case '770004':
            $title = $title ?: "支付{$code}，定时:更新代付订单表状态失败（无数据变化）";
            break;
        case '770005':
            $title = $title ?: "支付{$code}，代付金额退回失败";
            break;
        case '770006':
            $title = $title ?: "支付{$code}，代付插入通知数据库失败";
            break;
        case '770007':
            $title = $title ?: "支付{$code}，多次通知:在一秒中多次通知相同订单";
            break;
        case '770008':
            $title = $title ?: "支付{$code}，代付子公司配置不存在";
            break;
        case '770009':
            $title = $title ?: "支付{$code}，代付订单更新状态错误";
            break;
        case '770010':
            $title = $title ?: "支付{$code}，定时:更新代付订单表状态失败（无数据变化）";
            break;
        case '770011':
            $title = $title ?: "支付{$code}，代付金额退回失败";
            break;
        case '770012':
            $title = $title ?: "支付{$code}，代付插入通知数据库失败";
            break;
        case '770013':
            $title = $title ?: "支付{$code}，银行卡代付失败处理";
            break;
        case '770014':
            $title = $title ?: "支付{$code}，代付失败处理更新异常";
            break;
        case '770015':
            $title = $title ?: "支付{$code}，代付金额退回失败";
            break;
        case '770016':
            $title = $title ?: "支付{$code}，代付插入通知数据库失败";
            break;

        case '780000':
            $title = $title ?: "支付{$code}，查询代付余额失败";
            break;

        case '800000':
            $title = $title ?: "支付{$code}，发送邮箱验证码失败";
            break;
        case '800001':
            $title = $title ?: "支付{$code}，发送手机号验证码失败";
            break;
        case '800002':
            $title = $title ?: "支付{$code}，模拟第三方代付订单退回，扣除商户代付余额(还原)失败";
            break;
        case '800003':
            $title = $title ?: "支付{$code}，模拟第三方代付订单退回，插入多次通知数据库失败";
            break;
        case '800004':
            $title = $title ?: "支付{$code}，模拟第三方代付订单退回，扣除商户代付余额(还原)失败";
            break;
        case '800005':
            $title = $title ?: "支付{$code}，模拟第三方代付订单退回，插入多次通知数据库失败";
            break;

        case '810000':
            $title = $title ?: "支付{$code}，后台更新单个商户过渡金额异常";
            break;
        case '810001':
            $title = $title ?: "支付{$code}，后台更新所有商户过渡金额异常";
            break;

        case '820000':
            $title = $title ?: "支付{$code}，增加钱包操作异常";
            break;
        case '820001':
            $title = $title ?: "支付{$code}，减少钱包操作异常";
            break;
        case '820002':
            $title = $title ?: "支付{$code}，减少钱包操作异常";
            break;

        case '830000':
            $title = $title ?: "支付{$code}，发送邮箱验证码失败";
            break;
        case '830001':
            $title = $title ?: "支付{$code}，发送手机号验证码失败";
            break;
        case '830002':
            $title = $title ?: "支付{$code}，模拟第三方代付订单退回，扣除商户代付余额(还原)失败";
            break;
        case '830003':
            $title = $title ?: "支付{$code}，模拟第三方代付订单退回，插入多次通知数据库失败";
            break;
        case '830004':
            $title = $title ?: "支付{$code}，模拟第三方代付订单退回，扣除商户代付余额(还原)失败";
            break;
        case '830005':
            $title = $title ?: "支付{$code}，模拟第三方代付订单退回，插入多次通知数据库失败";
            break;

        case '840000':
            $title = $title ?: "支付{$code}，代付转账失败";
            break;

        case '850000':
            $title = $title ?: "支付{$code}，单次提现更新金额有误，请联系程序员处理";
            break;
        case '850001':
            $title = $title ?: "支付{$code}，支付宝提现：单次提现更新金额有误，请联系程序员处理";
            break;
        case '850002':
            $title = $title ?: "支付{$code}，导入出错了，请联系程序员处理";
            break;
        case '850003':
            $title = $title ?: "支付{$code}，提现更新金额有误，请联系程序员处理";
            break;
        case '850004':
            $title = $title ?: "支付{$code}，状态已经修改,但是商户冻结金额不足,请联系程序员处理：代号100";
            break;
        case '850005':
            $title = $title ?: "支付{$code}，状态已经修改,但是商户冻结金额扣除失败,请联系程序员处理：代号200";
            break;
        case '850006':
            $title = $title ?: "支付{$code}，状态已经修改,但是商户冻结金额不足,请联系程序员处理：代号300";
            break;
        case '850007':
            $title = $title ?: "支付{$code}，状态已经修改,但是提现冻结金额恢复有误,请联系程序员处理：代号400";
            break;
        case '850008':
            $title = $title ?: "支付{$code}，更新提现状态失败：代号500";
            break;
        case '850009':
            $title = $title ?: "支付{$code}，支付宝提现：状态已经修改,但是商户冻结金额不足,请联系程序员处理：代号100";
            break;
        case '850010':
            $title = $title ?: "支付{$code}，支付宝提现：状态已经修改,但是商户冻结金额扣除失败,请联系程序员处理：代号200";
            break;
        case '850011':
            $title = $title ?: "支付{$code}，支付宝提现：状态已经修改,但是商户冻结金额不足,请联系程序员处理：代号300";
            break;
        case '850012':
            $title = $title ?: "支付{$code}，支付宝提现：状态已经修改,但是提现冻结金额恢复有误,请联系程序员处理：代号400";
            break;
        case '850013':
            $title = $title ?: "支付{$code}，支付宝提现：更新提现状态失败：代号500";
            break;

        case '860000':
            $title = $title ?: "支付{$code}，接口提现操作异常";
            break;
        case '860001':
            $title = $title ?: "支付{$code}，更新接口提现操作异常";
            break;

        case '870000':
            $title = $title ?: "支付{$code}，收款操作异常";
            break;
        case '870001':
            $title = $title ?: "支付{$code}，更新收款状态失败";
            break;

        case '870000':
            $title = $title ?: "支付{$code}，收款操作异常";
            break;
        case '870001':
            $title = $title ?: "支付{$code}，更新收款状态失败";
            break;

        case '900000':
            $title = $title ?: "支付{$code}，退款失败返回退款余额错误";
            break;
        case '900002':
            $title = $title ?: "支付{$code}，更新退款订单状态失败";
            break;
        case '900004':
            $title = $title ?: "支付{$code}，退款失败返回退款余额错误";
            break;
        case '900006':
            $title = $title ?: "支付{$code}，退款订单错误";
            break;
        case '900008':
            $title = $title ?: "支付{$code}，退款失败返回退款余额错误";
            break;
        case '900010':
            $title = $title ?: "支付{$code}，模拟退款失败返回退款余额错误";
            break;
        case '900012':
            $title = $title ?: "支付{$code}，退款订单通知失败";
            break;

        case '910000':
            $title = $title ?: "支付{$code}，退款查询通知错误";
            break;
        case '910001':
            $title = $title ?: "支付{$code}，在一秒中多次通知相同订单";
            break;
        case '910002':
            $title = $title ?: "支付{$code}，退款公司配置错误";
            break;
        case '910003':
            $title = $title ?: "支付{$code}，退款订单更新状态错误";
            break;
        case '910004':
            $title = $title ?: "支付{$code}，退款订单更新状态错误";
            break;
        case '910005':
            $title = $title ?: "支付{$code}，退款余额错误";
            break;
        case '910006':
            $title = $title ?: "支付{$code}，创建订单通知失败";
            break;

        case '999999':
            $title = $title ?: "支付{$code}，定时任务失败";
            break;

        default:
            $title = $title ?: "错误编码没有匹配";
            break;
    }
    $temp = is_array($temp) ? json_encode($temp, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $temp;
    $msg = is_array($msg) ? json_encode($msg, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $msg;
    $one = '自定义数据：' . $temp . PHP_EOL . PHP_EOL . '异常消息：' . $msg . PHP_EOL . PHP_EOL;
    $two = 'server参数：' . json_encode(request()->server(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $remark = $one . $two;
    // 写入错误日志
    if ($write) {
        Db::name('payment_error_log')->insert(['code' => $code, 'title' => $title, 'remark' => $remark, 'create_time' => time()]);
    }
    // 发送邮件
    if ($tomail) {
        send_mail_error($title, $remark);
    }
    return true;
}




/**
 * VPN重要错误日志记录
 * @param  string   $code        错误编码
 * @param  string   $temp        自定义数据
 * @param  string   $msg         异常信息
 * @param  boolean  $tomail      是否发送邮件
 * @param  boolean  $write       是否写入错误日志
 * @param  string   $title       标题
 * @return boolean
 */
function vpn_error_log($code = '', $temp = '', $msg = '', $tomail = false, $write = true, $title = '')
{
    $module_title = 'vpn' . $code;
    switch ($code) {

        case '500000':
            $title = $title ?: $module_title . "curl错误";
            break;

        case '110000':
            $title = $title ?: $module_title . "用户登录异常";
            break;

        case '110002':
            $title = $title ?: $module_title . "获取用户信息";
            break;

        case '110004':
            $title = $title ?: $module_title . "用户注册异常";
            break;

        case '110006':
            $title = $title ?: $module_title . "发送邮箱验证码异常";
            break;

        case '110008':
            $title = $title ?: $module_title . "发送手机号验证码异常";
            break;

        case '110010':
            $title = $title ?: $module_title . "发送验证码异常";
            break;

        case '110012':
            $title = $title ?: $module_title . "修改密码异常";
            break;

        case '110014':
            $title = $title ?: $module_title . "签到异常";
            break;

        case '110016':
            $title = $title ?: $module_title . "邀请列表异常";
            break;

        case '110018':
            $title = $title ?: $module_title . "系统配置异常";
            break;

        case '110020':
            $title = $title ?: $module_title . "链接异常";
            break;

        case '110022':
            $title = $title ?: $module_title . "节点异常";
            break;

        case '110024':
            $title = $title ?: $module_title . "支付方式异常";
            break;

        case '110026':
            $title = $title ?: $module_title . "商品列表异常";
            break;

        case '110028':
            $title = $title ?: $module_title . "创建订单异常";
            break;

        case '110030':
            $title = $title ?: $module_title . "苹果订单异常";
            break;

        case '110032':
            $title = $title ?: $module_title . "订单更新异常";
            break;

        case '110034':
            $title = $title ?: $module_title . "订单用户更新异常";
            break;

        case '110038':
            $title = $title ?: $module_title . "订单列表异常";
            break;

        case '110040':
            $title = $title ?: $module_title . "添加设备异常";
            break;

        case '110042':
            $title = $title ?: $module_title . "删除设备异常";
            break;

        case '110044':
            $title = $title ?: $module_title . "反馈异常";
            break;

        case '110046':
            $title = $title ?: $module_title . "反馈列表异常";
            break;

        case '110048':
            $title = $title ?: $module_title . "流量记录异常";
            break;

        case '110050':
            $title = $title ?: $module_title . "删除缓存异常";
            break;


        case '900001':
            $title = $title ?: $module_title . "支付成功用户数据更新失败";
            break;


        default:
            $title = $title ?: "错误编码没有匹配";
            break;
    }
    $temp = is_array($temp) ? json_encode($temp, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $temp;
    $msg = is_array($msg) ? json_encode($msg, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $msg;
    $one = '自定义数据：' . $temp . PHP_EOL . PHP_EOL . '异常消息：' . $msg . PHP_EOL . PHP_EOL;
    $two = 'server参数：' . json_encode(request()->server(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $remark = $one . $two;
    // 写入错误日志
    if ($write) {
        Db::name('vpn_log')->insert(['code' => $code, 'title' => $title, 'remark' => $remark, 'create_time' => time()]);
    }
    // 发送邮件
    if ($tomail) {
        send_mail_error($title, $remark);
    }
    return true;
}
